/*
    Android UIML

	Copyright (C) 2010  Bram Goffings (bramgoffings@gmail.com)

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public License
	as published by the Free Software Foundation; either version 2.1
	of	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

package uiml.android.elements.peers;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import uiml.android.elements.Logic;
import uiml.android.main.AndroidUiml;
import uiml.android.parser.DomAndroidUimlParser;
import uiml.android.template.Template;

public final class Vocabulary {
	
	// a Hashtable of (name, DClass) key-value pairs
	protected Hashtable<String, DClass> dictDCls = new Hashtable<String, DClass>(); 
	// a Hashtable of (name, DComponent) key-value pairs
	protected Hashtable<String, DComponent> dictDCmp = new Hashtable<String, DComponent>(); 

	
	// TODO comment
	protected String identifier = null;	
	protected String vocName = null;
	
	protected Document doc = null;
	
	
	/**
	 * Singleton vocabulary class
	 */
	
	// Singleton instance
	private static Vocabulary instance = null;
	
	/**
	 * 
	 * @param vocName
	 */
	protected Vocabulary(String vocName) {
	   this.vocName = vocName;	
	   Load(vocName);
	}
	
	/**
	 * 
	 * @param vocName
	 * @return
	 */
	public static Vocabulary getInstance(String vocName) {
	   if(instance == null) {
	      instance = new Vocabulary(vocName);
	   }
	   return instance;
	}
	
	/**
	 * 
	 * @param vocName
	 */
	public void Load(String vocName)
	{
		try {
			
			String pack = AndroidUiml.me.getClass().getPackage().getName();
			
			int resID = AndroidUiml.me.getResources().getIdentifier(vocName, "raw", pack);
			Document doc = new DomAndroidUimlParser(AndroidUiml.me.getResources().openRawResource(resID)).parse();
			Element rootElement = (Element) doc.getDocumentElement();
			process(rootElement);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 
	 * @param elem
	 */
	protected void process(Element elem)
	{
		NodeList childItems = elem.getChildNodes();
		
		if(childItems != null)
		{
			for(int i=0; i<childItems.getLength(); ++i) {
				Node child = childItems.item(i);	
				if(child.getNodeType() == Node.ELEMENT_NODE)
					processElement((Element)child);
			}
		}
	}
	
	/**
	 * 
	 * @param elem
	 */
	protected void processElement(Element elem)
	{
		if(elem.getTagName().equals(Template.IAM))
			new Template(elem);
		else if(elem.getTagName().equals(Presentation.IAM))
			processPresentation(elem);
		else if(elem.getTagName().equals(Logic.IAM))
			processLogic(elem);
	}

	/**
	 * 
	 * @param elem
	 */
	protected void processLogic(Element elem) {
		
		Logic l = new Logic(elem);

		for(DComponent dcmp : l.getComponents())
			addComponent(dcmp.getIdentifier(), dcmp);
	}
	
	public void addLogic(Logic l) {
		for(DComponent dcmp : l.getComponents())
			addComponent(dcmp.getIdentifier(), dcmp);
	}

	/**
	 * 
	 * @param elem
	 */
	protected void processPresentation(Element elem) {
		
		Presentation p = new Presentation(elem);
		identifier = p.getBase();

		for(DClass dclass : p.getComponents())
			addClass(dclass.getIdentifier(), dclass);
		
	}

	/**
	 * 
	 * @param abstractName
	 * @param dclass
	 */
	private void addClass(String abstractName, DClass dclass) {

			DClass oldDClass = dictDCls.put(abstractName, dclass);
		 
			if(oldDClass!=null)
				System.out.println("Check vocabulary, there are duplicate abstractNames");
	}
	
	/**
	 * 
	 * @param abstractName
	 * @param dclass
	 */
	private void addComponent(String abstractName, DComponent dcomponent) {

			DComponent oldDComponent = dictDCmp.put(abstractName, dcomponent);
		 
			if(oldDComponent!=null)
				System.out.println("Check vocabulary, there are duplicate abstractNames");
	}
	
	/**
	 * 
	 * @param abstractName
	 * @return
	 */
	protected DClass findDClass(String abstractName) {
		return dictDCls.get(abstractName);
	}
	
	/**
	 * 
	 * @param abstractName
	 * @return
	 */
	public DComponent findDComponent(String abstractName) {
		return dictDCmp.get(abstractName);
	}
	
	/**
	 * 
	 * @param abstractName
	 * @return
	 */
	public String mapsOnCls(String abstractName)
	{
		DClass dclass = findDClass(abstractName);
		
		if(dclass == null)
			System.out.println("DClass not found: " + abstractName);
			
		return dclass.getMapsTo();
	}
	
	/**
	 * 
	 * @param abstractName
	 * @return
	 */
	public String mapsOnComponent(String abstractName)
	{
		DComponent dcmp = findDComponent(abstractName);
		
		if(dcmp == null)
			System.out.println("DComponent not found: " + abstractName);
			
		return dcmp.getMapsTo();
	}
	
	/**
	 * 
	 * @param identifier
	 * @param abstractName
	 * @param mapsType
	 * @return
	 */
    public DProperty getDProperty(String identifier, String abstractName, String mapsType)
    {
        DClass dclass = findDClass(abstractName);

        for(DProperty property : dclass.getDProperties()) {
        	if(property.getId().equals(identifier) && property.getMapsType().equals(mapsType))
        		return property;
        }
        
        System.out.println("DProperty not found: " + identifier + "," + abstractName + "," + mapsType);
       	return null;
    }
    
	/**
	 * 
	 * @param identifier
	 * @param abstractName
	 * @param mapsType
	 * @return
	 */
    public DMethod getDMethod(String identifier, String abstractName)
    {
    	DComponent dcmp = findDComponent(abstractName);

        for(DMethod method : dcmp.getDMethods()) {
        	if(method.getIdentifier().equals(identifier))
        		return method;
        }
        
        System.out.println("DMethod not found: " + identifier + "," + abstractName);
       	return null;
    }
    
    /**
     * 
     * @param identifier
     * @param abstractName
     * @param mapsType
     * @return
     */
	public String getProperty(String identifier, String abstractName, String mapsType)
	{
        return getDProperty(identifier, abstractName, mapsType).getMapsTo();
	}
	
    /**
     * 
     * @param identifier
     * @param abstractName
     * @param mapsType
     * @return
     */
	public String getMethod(String identifier, String abstractName)
	{
        return getDMethod(identifier, abstractName).getMapsTo();
	}

}
